package lexy

import "markers"
import "unicode"

type Byte struct {rune int; line, col int}

func ReadBytes( sink chan Byte, stuff [][]byte ) {
    lastLine, lastCol := 1, 1
    for i, line := range stuff {
        for j, ch := range line {
            if ch != ' ' { lastLine, lastCol = i + 1, j + 1 } 
            sink<-Byte{int(ch), i + 1, j + 1} 
        }
        sink<-Byte{'\n', i + 1, lastCol }
    }
    sink<-Byte{0, lastLine, lastCol}
    close(sink)
}

func ReadTokens( sink chan(Token), source chan(Byte) ) {
    defer close(sink)
    ch := <-source
    for {
        spelling := ""
        for ch.rune == ' ' || ch.rune == '\n' { ch = <-source }
        line, col := ch.line, ch.col
        // fmt.Printf( "line, col = %v, %v\n", line, col )
        switch {                
            case unicode.IsLetter(ch.rune):
                for unicode.IsLetter(ch.rune) {
                    spelling += string(ch.rune)
                    ch = <-source
                }
                sink<-Token{ID, spelling, markers.Point{line, col}}

            case unicode.IsDigit(ch.rune):
                for unicode.IsDigit(ch.rune) {
                    spelling += string(ch.rune)
                    ch = <-source
                }
                sink<-Token{NUM, spelling, markers.Point{line, col}}
                
            case ch.rune == '"':
                ch = <-source
                spelling += `"`
                for ch.rune != '"' { spelling += string(ch.rune); ch = <-source }
                spelling += `"`
                sink<-Token{STRING, spelling, markers.Point{line, col}}
                ch = <- source
                
            case ch.rune == '#':
                ch = <-source
                for ch.rune != '\n' { spelling += string(ch.rune); ch = <-source }
                sink<-Token{COMMENT, spelling, markers.Point{line, col}}
                
            case ch.rune == '(':
                sink<-Token{LPAR, "(", markers.Point{line, col}}
                ch = <-source
                
            case ch.rune == ')':
                sink<-Token{RPAR, ")", markers.Point{line, col}}
                ch = <-source
                
            case ch.rune == ';':
                sink<-Token{SEMI, ";", markers.Point{line, col}}
                ch = <-source
                
            case ch.rune == '[':
                sink<-Token{LBOX, "[", markers.Point{line, col}}
                ch = <-source
                
            case ch.rune == ']':
                sink<-Token{RBOX, "]", markers.Point{line, col}}
                ch = <-source
                
            case ch.rune == '.':
                sink<-Token{DOT, ".", markers.Point{line, col}}
                ch = <-source
                
            case ch.rune == '|':
                sink<-Token{BAR, "|", markers.Point{line, col}}
                ch = <-source
      
            case ch.rune == '+' || ch.rune == '-' || ch.rune == '*' || ch.rune == '/':
                sink<-Token{OP, string(ch.rune), markers.Point{line, col}}
                ch = <-source
      
            case ch.rune == ',':
                sink<-Token{COMMA, ",", markers.Point{line, col}}
                ch = <-source
      
            case ch.rune == '=':
                ch = <- source
                if ch.rune == '>' {
                    sink<-Token{ARROW, "=>", markers.Point{line, col}}                
                    ch = <-source
                } else {
                    sink<-Token{EQUALS, "=", markers.Point{line, col}}
                }
      
            case ch.rune == ':':
                sink<-Token{COLON, ":", markers.Point{line, col}}
                ch = <-source
                          
            default:
                if ch.rune == 0 {
                    sink<-Token{EOF, "<EOF>", markers.Point{line, col}} 
                    return 
                }
                sink<-Token{WRONG, string(ch.rune), markers.Point{line, col}}
                ch = <-source
        }
    }
}
 
type Class int

const (
    WRONG Class = iota
    ID
    NUM
    STRING
    LPAR
    RPAR
    LBOX
    RBOX
    DOT
    COMMA
    EQUALS
    COLON
    BAR
    OP
    COMMENT
    ARROW
    SEMI
    EOF
)

type Token struct {
    Class Class
    Spelling string
    Start markers.Point
}

type Source struct {
    Current Token
    Rest chan(Token)
    Markerz []markers.Marker
}

func (s *Source) Advance() {
    if s.Current.Class != EOF { s.Current = <- s.Rest }
}

func (s *Source) AddMarker( m markers.Marker ) {
    AddMarker( s, m )
}

func AddMarker( s *Source, m markers.Marker ) {
    if len(s.Markerz) == cap(s.Markerz) {
        panic( "bother" )
    }
    l := len(s.Markerz)
    s.Markerz = s.Markerz[0:l + 1]
    s.Markerz[l] = m
}
